﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using CoreEx.Common.Extensions;
using CoreEx.Common.Proxy;
using CoreEx.Database.Client;
using CoreEx.DynamicInvoker;
using LinFu.AOP.Interfaces;
using LinFu.IoC.Configuration;
using LinFu.IoC;
using LinFu.IoC.Interfaces;
using System.Data.Common;

namespace CoreEx.Database.Client.Implementation
{
    [Implements(typeof(IInvokeWrapper<IDbCommand>), LifecycleType.OncePerRequest)]
    public class CommandInvokeWrapper : InvokeWrapperBase<IDbCommand>
    {
        private IDbConnection _proxyConnection;
        private IDbTransaction _proxyTransaction;
        private IParameterExtractor _parameterExtractor;
        private IServiceContainer _serviceContainer;
        private bool _implicitConnection;
        private string _connectionName;

        public CommandInvokeWrapper(string connectionName, IDbCommand actualTarget)
            : base(actualTarget)
        {
            _connectionName = connectionName;
        }

        public override void BeforeInvoke(IInvocationInfo info)
        {        
            base.BeforeInvoke(info);
        }

        

        



        public override object DoInvoke(IInvocationInfo info)
        {
            if (info.TargetMethod.Name == "get_Connection")
            {
                return _proxyConnection;
            }
            if (info.TargetMethod.Name == "set_Connection")
            {                                              
                _proxyConnection = (IDbConnection)info.Arguments[0];
                var actualConnection = ((IActualTarget)info.Arguments[0]).Instance;
                return info.TargetMethod.DynamicInvoke(ActualTarget, new []{actualConnection});                
                
            }

            if (info.TargetMethod.Name == "get_Transaction")
            {
                return _proxyTransaction;
            }
            if (info.TargetMethod.Name == "set_Transaction")
            {              
                _proxyTransaction = (IDbTransaction) info.Arguments[0];
                if (_proxyTransaction == null)
                    info.TargetMethod.DynamicInvoke(ActualTarget, new[] { _proxyTransaction });
                else
                {
                    var actualTransaction = ((IActualTarget) info.Arguments[0]).Instance;
                    return info.TargetMethod.DynamicInvoke(ActualTarget, new[] {actualTransaction});
                }

            }

            if (info.TargetMethod.DeclaringType == typeof(IDisposable))
            {
                if (((INamedConnection)_proxyConnection).IsInternal)
                {
                    var connection = ActualTarget.Connection;
                    if (connection.State == ConnectionState.Open)
                        connection.Close();
                    connection.Dispose();
                    ActualTarget.Connection = null;
                    _proxyConnection = null;
                }
            }


            return base.DoInvoke(info);
        }

        public override void AfterInvoke(IInvocationInfo info, object returnValue)
        {
            if (info.TargetMethod.Name == "set_CommandText"
                || info.TargetMethod.Name == "set_CommandType"
                || info.TargetMethod.Name == "set_Connection")
            {
                if (ActualTarget.Connection != null && ActualTarget.CommandType == CommandType.StoredProcedure)
                {
                    ActualTarget.Parameters.Clear();

                    var parameters = _parameterExtractor.GetParameters(ActualTarget.CommandText);
                    parameters.ForEach(p => ActualTarget.Parameters.Add(p));
                    
                }
            }
         
            base.AfterInvoke(info, returnValue);
        }

       

       

        public override void Initialize(IServiceContainer source)
        {            
            base.Initialize(source);
            _parameterExtractor = source.GetService<IParameterExtractor>(string.Empty, _connectionName);   
        }
        

    }
}
